package com.zengjianjun.oss.platform.amazon;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson2.JSON;
import com.zengjianjun.oss.enums.OssPlatformEnum;
import com.zengjianjun.oss.exception.OssException;
import com.zengjianjun.oss.platform.AbstractOssClient;
import com.zengjianjun.oss.platform.param.FileListParam;
import com.zengjianjun.oss.platform.result.FileListResult;
import com.zengjianjun.oss.properties.AmazonS3Properties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.http.apache.ProxyConfiguration;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3ClientBuilder;
import software.amazon.awssdk.services.s3.model.*;

import java.io.File;
import java.net.URI;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zengjianjun
 */
@Slf4j
public class AmazonOssClient extends AbstractOssClient {

    private final S3Client ossClient;

    public AmazonOssClient(AmazonS3Properties amazonS3Properties) {
        try {
            Region region = amazonS3Properties.getRegion();
            String accessKeyId = amazonS3Properties.getAccessKeyId();
            String accessKeySecret = amazonS3Properties.getAccessKeySecret();
            Boolean proxy = amazonS3Properties.getProxy();
            String proxyEndpoint = amazonS3Properties.getProxyEndpoint();
            log.info("init {} oss start: region = {}, accessKeyId = {}, secretAccessKey = {}", get().name(), region.id(), accessKeyId, accessKeySecret);
            // 创建 AwsBasicCredentials 对象
            AwsBasicCredentials awsCreds = AwsBasicCredentials.create(accessKeyId, accessKeySecret);
            S3ClientBuilder s3ClientBuilder = S3Client.builder()
                    .region(region)
                    .credentialsProvider(StaticCredentialsProvider.create(awsCreds));
            if (proxy != null && proxy) {
                // 配置代理
                ProxyConfiguration proxyConfig = ProxyConfiguration.builder()
                        // 代理主机
                        .endpoint(URI.create(proxyEndpoint))
                        .build();
                // 配置 Apache HTTP 客户端
                ApacheHttpClient.Builder httpClientBuilder = ApacheHttpClient.builder()
                        .proxyConfiguration(proxyConfig);
                s3ClientBuilder.httpClientBuilder(httpClientBuilder);
            }
            this.ossClient = s3ClientBuilder.build();
            log.info("init {} oss success......", get().name());
        } catch (Exception e) {
            log.error("init {} oss error：", get().name(), e);
            throw OssException.build(e);
        }
    }

    @Override
    protected OssPlatformEnum get() {
        return OssPlatformEnum.amazon;
    }

    @Override
    public List<FileListResult> getFileList(FileListParam param) {
        log.info("[{}] 获取文件文件列表开始：param = {}", get().name(), JSON.toJSONString(param));
        long startTime = System.currentTimeMillis();
        ListObjectsV2Request listObjects = ListObjectsV2Request
                .builder()
                .bucket(param.getBucketName())
                .prefix(param.getPrefix())
                .startAfter(param.getStartAfter())
                .maxKeys(param.getMaxKeys())
                .delimiter(param.getDelimiter())
                .build();
        ListObjectsV2Response result = this.ossClient.listObjectsV2(listObjects);
        List<FileListResult> list = new ArrayList<>(result.contents().size());
        for (S3Object data : result.contents()) {
            if (StringUtils.isNotEmpty(param.getEndFinish()) && data.key().compareTo(param.getEndFinish()) >= 0) {
                break;
            }
            list.add(FileListResult.builder()
                    .bucketName(param.getBucketName())
                    .key(data.key())
                    .size(data.size())
                    .build());
        }
        log.info("[{}] 获取文件文件列表结束：耗时{}ms", get().name(), System.currentTimeMillis() - startTime);
        return list;
    }

    @Override
    public FileListResult getFile(String bucketName, String fileKey) {
        // 获取全部元数据可以使用：ObjectMetadata metadata = ossClient.getObjectMetadata(bucketName, objectName);
        log.info("[{}] 获取文件原数据：bucketName = {}, fileKey = {}", get().name(), bucketName, fileKey);
        long startTime = System.currentTimeMillis();
        // 创建 HeadObjectRequest 以获取文件的元数据
        HeadObjectRequest headObjectRequest = HeadObjectRequest.builder()
                .bucket(bucketName)
                .key(fileKey)
                .build();
        // 调用 headObject 方法获取元数据
        HeadObjectResponse response = this.ossClient.headObject(headObjectRequest);
        long endTime = System.currentTimeMillis();
        log.info("[{}] 获取文件原数据：result = {}, 耗时{}ms", get().name(), JSON.toJSONString(response), endTime - startTime);
        return FileListResult.builder()
                .bucketName(bucketName)
                .size(response.contentLength())
                .key(fileKey)
                .build();
    }

    @Override
    public List<FileListResult> getFullFileList(FileListParam param) {
        // 不提供返回所有的文件列表的接口
        throw OssException.build("不提供返回所有的文件列表的接口");
    }

    @Override
    public void download(String bucketName, String fileKey, String filePath) {
        // 下载Object到本地文件，并保存到指定的本地路径中。
        log.info("[{}] 下载文件到本地: bucketName = {}, fileKey = {}, filePath = {}", get().name(), bucketName, fileKey, filePath);
        long startTime = System.currentTimeMillis();
        // 若父目录不存在创建父目录
        File file = new File(filePath);
        if (!file.getParentFile().exists()) {
            FileUtil.mkdir(file.getParentFile());
        }
        // 若文件存在，删除原文件
        if (file.exists()) {
            FileUtil.del(file);
        }
        GetObjectRequest getObjectRequest = GetObjectRequest
                .builder()
                .bucket(bucketName)
                .key(fileKey)
                .build();
        this.ossClient.getObject(getObjectRequest, Paths.get(filePath));
        long endTime = System.currentTimeMillis();
        log.info("[{}] 下载文件到本地: 耗时{}ms", get().name(), endTime - startTime);
    }

    @Override
    public void upload(String bucketName, String fileKey, String filePath) {
        long startTime = System.currentTimeMillis();
        log.info("[{}] 文件上传: bucketName = {}, fileKey = {}", get().name(), bucketName, fileKey);
        // 创建 PutObjectRequest 对象
        PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(fileKey)
                .build();
        this.ossClient.putObject(putObjectRequest, RequestBody.fromFile(Paths.get(filePath)));
        long endTime = System.currentTimeMillis();
        log.info("[{}] 文件上传结束: 耗时{}ms", get().name(), endTime - startTime);
    }

    @Override
    public void upload(String bucketName, String fileKey, File file) {
        this.upload(bucketName, fileKey, file.getPath());
    }

    @Override
    public void delete(String bucketName, String fileKey) {
        long startTime = System.currentTimeMillis();
        log.info("[{}] 文件删除: bucketName = {}, fileKey = {}", get().name(), bucketName, fileKey);
        // 创建 DeleteObjectRequest 对象
        DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
                .bucket(bucketName)
                .key(fileKey)
                .build();
        this.ossClient.deleteObject(deleteObjectRequest);
        long endTime = System.currentTimeMillis();
        log.debug("[{}] 文件删除结束: 耗时{}ms", get().name(), endTime - startTime);
    }
}
